/*
 * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.interceptors;

import org.omg.PortableInterceptor.Interceptor;
import org.omg.PortableInterceptor.ORBInitInfo;
import org.omg.PortableInterceptor.ORBInitInfoPackage.DuplicateName;

import org.omg.CORBA.INTERNAL;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.lang.reflect.Array;

import com.sun.corba.se.impl.logging.InterceptorsSystemException;

/**
 * Provides a repository of registered Portable Interceptors, organized
 * by type.  This list is designed to be accessed as efficiently as
 * possible during runtime, with the expense of added complexity during
 * initialization and interceptor registration.  The class is designed
 * to easily allow for the addition of new interceptor types.
 */
public class InterceptorList {

  // Interceptor type list.  If additional interceptors are needed,
  // add additional types in numerical order (do not skip numbers),
  // and update NUM_INTERCEPTOR_TYPES and classTypes accordingly.
  // NUM_INTERCEPTOR_TYPES represents the number of interceptor
  // types, so we know how many lists to maintain.
  static final int INTERCEPTOR_TYPE_CLIENT = 0;
  static final int INTERCEPTOR_TYPE_SERVER = 1;
  static final int INTERCEPTOR_TYPE_IOR = 2;

  static final int NUM_INTERCEPTOR_TYPES = 3;

  // Array of class types for interceptors.  This is used to create the
  // appropriate array type for each interceptor type.  These must
  // match the indices of the constants declared above.
  static final Class[] classTypes = {
      org.omg.PortableInterceptor.ClientRequestInterceptor.class,
      org.omg.PortableInterceptor.ServerRequestInterceptor.class,
      org.omg.PortableInterceptor.IORInterceptor.class
  };

  // True if no further interceptors may be registered with this list.
  private boolean locked = false;
  private InterceptorsSystemException wrapper;

  // List of interceptors currently registered.  There are
  // NUM_INTERCEPTOR_TYPES lists of registered interceptors.
  // For example, interceptors[INTERCEPTOR_TYPE_CLIENT] contains an array
  // of objects of type ClientRequestInterceptor.
  private Interceptor[][] interceptors =
      new Interceptor[NUM_INTERCEPTOR_TYPES][];

  /**
   * Creates a new Interceptor List.  Constructor is package scope so
   * only the ORB can create it.
   */
  InterceptorList(InterceptorsSystemException wrapper) {
    this.wrapper = wrapper;
    // Create empty interceptors arrays for each type:
    initInterceptorArrays();
  }

  /**
   * Registers an interceptor of the given type into the interceptor list.
   * The type is one of:
   * <ul>
   * <li>INTERCEPTOR_TYPE_CLIENT - ClientRequestInterceptor
   * <li>INTERCEPTOR_TYPE_SERVER - ServerRequestInterceptor
   * <li>INTERCEPTOR_TYPE_IOR - IORInterceptor
   * </ul>
   *
   * @throws DuplicateName Thrown if an interceptor of the given name already exists for the given
   * type.
   */
  void register_interceptor(Interceptor interceptor, int type)
      throws DuplicateName {
    // If locked, deny any further addition of interceptors.
    if (locked) {
      throw wrapper.interceptorListLocked();
    }

    // Cache interceptor name:
    String interceptorName = interceptor.name();
    boolean anonymous = interceptorName.equals("");
    boolean foundDuplicate = false;
    Interceptor[] interceptorList = interceptors[type];

    // If this is not an anonymous interceptor,
    // search for an interceptor of the same name in this category:
    if (!anonymous) {
      int size = interceptorList.length;

      // An O(n) search will suffice because register_interceptor is not
      // likely to be called often.
      for (int i = 0; i < size; i++) {
        Interceptor in = (Interceptor) interceptorList[i];
        if (in.name().equals(interceptorName)) {
          foundDuplicate = true;
          break;
        }
      }
    }

    if (!foundDuplicate) {
      growInterceptorArray(type);
      interceptors[type][interceptors[type].length - 1] = interceptor;
    } else {
      throw new DuplicateName(interceptorName);
    }
  }

  /**
   * Locks this interceptor list so that no more interceptors may be
   * registered.  This method is called after all interceptors are
   * registered for security reasons.
   */
  void lock() {
    locked = true;
  }

  /**
   * Retrieves an array of interceptors of the given type.  For efficiency,
   * the type parameter is assumed to be valid.
   */
  Interceptor[] getInterceptors(int type) {
    return interceptors[type];
  }

  /**
   * Returns true if there is at least one interceptor of the given type,
   * or false if not.
   */
  boolean hasInterceptorsOfType(int type) {
    return interceptors[type].length > 0;
  }

  /**
   * Initializes all interceptors arrays to zero-length arrays of the
   * correct type, based on the classTypes list.
   */
  private void initInterceptorArrays() {
    for (int type = 0; type < NUM_INTERCEPTOR_TYPES; type++) {
      Class classType = classTypes[type];

      // Create a zero-length array for each type:
      interceptors[type] =
          (Interceptor[]) Array.newInstance(classType, 0);
    }
  }

  /**
   * Grows the given interceptor array by one:
   */
  private void growInterceptorArray(int type) {
    Class classType = classTypes[type];
    int currentLength = interceptors[type].length;
    Interceptor[] replacementArray;

    // Create new array to replace the old one.  The new array will be
    // one element larger but have the same type as the old one.
    replacementArray = (Interceptor[])
        Array.newInstance(classType, currentLength + 1);
    System.arraycopy(interceptors[type], 0,
        replacementArray, 0, currentLength);
    interceptors[type] = replacementArray;
  }

  /**
   * Destroys all interceptors in this list by invoking their destroy()
   * method.
   */
  void destroyAll() {
    int numTypes = interceptors.length;

    for (int i = 0; i < numTypes; i++) {
      int numInterceptors = interceptors[i].length;
      for (int j = 0; j < numInterceptors; j++) {
        interceptors[i][j].destroy();
      }
    }
  }

  /**
   * Sort interceptors.
   */
  void sortInterceptors() {
    List sorted = null;
    List unsorted = null;

    int numTypes = interceptors.length;

    for (int i = 0; i < numTypes; i++) {
      int numInterceptors = interceptors[i].length;
      if (numInterceptors > 0) {
        // Get fresh sorting bins for each non empty type.
        sorted = new ArrayList(); // not synchronized like we want.
        unsorted = new ArrayList();
      }
      for (int j = 0; j < numInterceptors; j++) {
        Interceptor interceptor = interceptors[i][j];
        if (interceptor instanceof Comparable) {
          sorted.add(interceptor);
        } else {
          unsorted.add(interceptor);
        }
      }
      if (numInterceptors > 0 && sorted.size() > 0) {
        // Let the RuntimeExceptions thrown by sort
        // (i.e., ClassCastException and UnsupportedOperationException)
        // flow back to the user.
        Collections.sort(sorted);
        Iterator sortedIterator = sorted.iterator();
        Iterator unsortedIterator = unsorted.iterator();
        for (int j = 0; j < numInterceptors; j++) {
          if (sortedIterator.hasNext()) {
            interceptors[i][j] =
                (Interceptor) sortedIterator.next();
          } else if (unsortedIterator.hasNext()) {
            interceptors[i][j] =
                (Interceptor) unsortedIterator.next();
          } else {
            throw wrapper.sortSizeMismatch();
          }
        }
      }
    }
  }
}
